﻿using System.Collections.Generic;
using Core.Log;
using Mono.Cecil;
using UnityEngine;
using Util;

namespace Core.Utils
{
    public class Pool {
        private string mName;
        private GameObject mRecord;
        private Stack<GameObject> mPooledObjects;

        public Pool(string Name,GameObject obj) {
            mName = Name;
            mPooledObjects = new Stack<GameObject>();
            mRecord = obj;
            obj.transform.SetParent(PoolMgr.Ins.PoolRoot);
            obj.SetActive(false);
        }

        public void Push(GameObject obj) {
            obj.transform.SetParent(PoolMgr.Ins.PoolRoot);
            obj.SetActive(false);
            mPooledObjects.Push(obj);
        }

        public GameObject Pop() {
            GameObject obj;
            if (mPooledObjects.Count > 0) {
                obj = mPooledObjects.Pop();
            } else {
                obj = PoolMgr.Ins.InstantiateObject(mRecord);
            }

            obj.name = mName;
            obj.SetActive(true);
            return obj;
        }
    }
    /// <summary>
    /// 对象池管理器
    /// </summary>
    public class PoolMgr : MonoSingleton<PoolMgr> {
        public Transform PoolRoot {
            get;
            private set;
        }

        private Dictionary<string, Pool> mPoolDic = new();
        protected override void OnCreate() {
            PoolRoot = GameObject.Find("GameObjectPool")?.transform;
            if (!PoolRoot) {
                PoolRoot = new GameObject("GameObjectPool").transform;
            }
        }

        ///GameObject对象池
        public GameObject Spawn(string name) {
            if (mPoolDic.TryGetValue(name, out Pool pool)) { 
                return pool.Pop();
            }

            var obj = AssetLoader.InstantiateGameObject(name);
            pool = CreatePool(name,obj);
            
            return pool.Pop();
        }

        public GameObject Copy(GameObject obj) {
            if (mPoolDic.TryGetValue(obj.name, out Pool pool)) { 
                return pool.Pop();
            }
            pool = CreatePool(obj.name,obj);
            
             return pool.Pop();
        }

        public GameObject InstantiateObject(GameObject obj) {
            return Instantiate(obj);
        }
        
        public void Release(GameObject obj)
        {
            if (mPoolDic.TryGetValue(obj.name, out Pool pool)) {
                pool.Push(obj);
                return;
            }
            ADbg.LogWF("[Pool] 对象 {0} 正在被放回缓存池，但是并不存在属于它的池子，已强制清理", obj.name);
            Destroy(obj);
        }

        public Pool CreatePool(GameObject obj) {
            return CreatePool(obj.name, obj);
        }
        
        public Pool CreatePool(string name,GameObject obj) {
            if (mPoolDic.ContainsKey(name)) {
                return mPoolDic[name];
            }
            Pool pool = new Pool(name,obj);
            mPoolDic.Add(name,pool);
            return pool;
        }
        
        #region 非Unity对象

        public T Get<T>() where T : class, new() {
            return ObjectPool<T>.Create();
        }

        public void Release<T>(T obj) where T :class,IResetable,new() {
            obj.Reset();
            ObjectPool<T>.Release(obj);
        }
        #endregion
    }
}